package com.cn.boot.student.service.rbac.impl;

import com.cn.boot.student.service.common.impl.RedisServiceImpl;
import com.cn.boot.student.util.CommonResult;
import com.cn.boot.student.common.JwtTokenUtil;
import com.cn.boot.student.entity.PermissionEntity;
import com.cn.boot.student.entity.UserEntity;
import com.cn.boot.student.mapper.UserMapper;
import com.cn.boot.student.service.rbac.IUserService;
import com.cn.boot.student.util.Tool;
import com.cn.boot.student.util.UploadFile;
import com.cn.boot.student.vo.LoginParams;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 用户逻辑实现层
 *
 * @author Created by luosir in 2021-02-02
 */
@Service
@Transactional
//开启redis缓存（user为缓存数据库中的文件夹名）
@CacheConfig(cacheNames = "user")
public class UserServiceImpl implements IUserService {
    //失败次数
    private final int FAILCOUNT = 5;
    //冻结时间
    private final int LOCKHOURS = 12;
    //失败时间
    private final int FAILTIME = 180;
    @Autowired
    private UserMapper userMapper;
    /**
     * 注入密码加密解密器
     */
    @Autowired
    private PasswordEncoder passwordEncoder;
    /**
     * 注入token生成工具类
     */
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private HttpServletRequest req;

    @Autowired
    private RedisServiceImpl redisService;

    @Override
    public UserEntity getUserByName(String name) {
        List<UserEntity> users = userMapper.getUserByName(name);
        Assert.isTrue(users.size() == 1, "账号或密码错误");
        return users.get(0);
    }

    @Override
    public List<PermissionEntity> getPermissionsByUserId(Integer id) {
        return userMapper.getPermissionByUserId(id);
    }

    /**
     * 登录逻辑
     *
     * @param loginParams
     * @return
     * @throws BadCredentialsException
     * @author LXJ
     * @date 2021/01/30
     */
    @Override
    public CommonResult login(LoginParams loginParams) throws BadCredentialsException {
        //接收前台传来的账号密码、验证码
        String username = loginParams.getUsername();
        String password = loginParams.getPassword();
        String code = loginParams.getCode();
        //判断账号、密码是否为空
        if ("".equals(username)) {
            throw new BadCredentialsException("账号不能为空");
        }
        if ("".equals(password)) {
            throw new BadCredentialsException("密码不能为空");
        }
        if ("".equals(code)) {
            throw new BadCredentialsException("验证码不能为空");
        }
        //判断验证码是否正确,先得到session中的验证码
        String sessionCode = (String) req.getSession().getAttribute("code");
        if (!sessionCode.equalsIgnoreCase(code)) {
            throw new BadCredentialsException("验证码不正确！");
        }
        HashMap<String, Object> map = new HashMap<>();
        long lockTime = this.getUserLoginTimeLock(username);
        String key = "user:" + username + ":failCount";
        if (lockTime > 0) {//判断用户是否已经被锁定
            map.put("message", "该账号已经被锁定请在" + lockTime + "秒之后尝试");
            return CommonResult.failed(String.valueOf(map));
        }
        //验证完成后，查询账号得到一个实体userByName
        UserEntity userByName = getUserByName(username);
        //存在账号则对比密码(注入passwordEncoder密码验证器)
        boolean matches = passwordEncoder.matches(password, userByName.getPassword());
        if (!matches) {
            throw new BadCredentialsException("密码错误");
        }
        //账号密码验证成功，生成Token（注入JwtTokenUtil工具类）
        String token = jwtTokenUtil.generateToken(userByName);
        //返回token
        return CommonResult.success(token);
    }

    /**
     * 检查用户是否已经被锁定，如果是，返回剩余锁定时间，如果否，返回-1
     *
     * @param username username
     * @return 时间
     */
    private int getUserLoginTimeLock(String username) {
        String key = "user:" + username + ":lockTime";
        int lockTime = (int) redisService.getExpire(key);
        if (lockTime > 0) {//查询用户是否已经被锁定，如果是，返回剩余锁定时间，如果否，返回-1
            return lockTime;
        } else {
            return -1;
        }
    }

    /**
     * 设置失败次数
     */
    private void setFailCount(String username) {
        long count = this.getUserFailCount(username);
        String key = "user:" + username + "failCount";
        if (count < 0) {
            //判断redis中是否有该用户的失败登陆次数，如果没有，设置为1，过期时间为2分钟，如果有，则次数+1
            redisService.set(key, 1, FAILTIME);
        } else {
            redisService.incr(key, new Double(1));
        }
    }

    /**
     * 获取当前用户已失败次数
     *
     * @param username username
     * @return 已失败次数
     */
    private int getUserFailCount(String username) {
        String key = "user:" + username + ":failCount";
        //从redis中获取当前用户已失败次数
        Object object = redisService.get(key);
        if (object != null) {
            return (int) object;
        } else {
            return -1;
        }
    }

    /**
     * 查找用户是否存在
     */
    public Boolean findUserNameIsExists(String username) {
        List<UserEntity> users = userMapper.getUserByName(username);
        //如果存在，则返回true，否则返回false
        if (users.size() > 0) {
            return true;
        }
        return false;
    }


    /**
     * 注册
     */
    @Override
    public CommonResult register(UserEntity userEntity) {
        //接收前台传来的数据，判断用户是否已经存在
        Boolean bool = findUserNameIsExists(userEntity.getUsername());
        if (bool) {
            //若存在则返回存在标识符
            return CommonResult.success("isHave");
        }
        //当账号不存在时，则进行下一步
        //将密码加密
        String password = passwordEncoder.encode(userEntity.getPassword());
        userEntity.setPassword(password);
        //上传头像,将文件名保存到数据库中
        Integer of = userMapper.add(userEntity);
        return CommonResult.success(of);
    }

    /**
     * 用户列表
     *
     * @return
     */
//    @Cacheable(keyGenerator = "keyGenerator")
    @Override
    public CommonResult findAll(UserEntity userEntity) {
        //得到查询总数
        Integer count = userMapper.getCount(userEntity);
        //分页，条件查询
        PageHelper.startPage(Tool.toPageNum(userEntity.getPageNum()), Tool.toPageSize(userEntity.getPageSize()));
        List<UserEntity> list = userMapper.findAll(userEntity);
        PageInfo<UserEntity> pageInfo = new PageInfo<>(list);
        HashMap<String, Object> map = new HashMap<>();
        map.put("count", count);
        map.put("pageInfo", pageInfo);
        return CommonResult.success(map);
    }


    /**
     * 单条删除
     *
     * @param id
     * @return
     */
//    @CacheEvict(key = "'findAll[]'")
    @Override
    public CommonResult del(Integer id) {
        Integer result = userMapper.del(id);
        return CommonResult.success(result);
    }

    /**
     * 批量删除
     *
     * @param ids
     * @return
     */
//    @CacheEvict(key = "'findAll[]'")
    @Override
    public CommonResult dels(List<Integer> ids) {
        ids.stream().forEach(userMapper::del);
        return CommonResult.success(ids.size());
    }

    /**
     * 更新数据
     *
     * @param userEntity
     * @return
     */
    @Override
    public CommonResult update(UserEntity userEntity) {
        //调用文件上传工具类
        String string = UploadFile.upload(userEntity.getFile());
        //存入本地项目中
        if (string != null) {
            userEntity.setIcon(string);
        }
        Integer result = userMapper.update(userEntity);
        if (result == 0) {
            return CommonResult.failed("更新失败！");
        }
        return CommonResult.success(result, "更新成功！");
    }

    /**
     * 根据id查找用户
     *
     * @param id
     * @return
     */
    @Override
    public CommonResult findById(Integer id) {
        UserEntity UserEntity = userMapper.findById(id);
        return CommonResult.success(UserEntity);
    }

    /**
     * 批量添加
     *
     * @param users
     * @return
     */
    @Override
    public CommonResult adds(List<UserEntity> users) {
        while (users != null) {
            UserEntity userEntity = getUserByName(users.get(0).getUsername());
            if (userEntity != null) {
                return CommonResult.failed("当前用户已被注册，请重新输入！");
            } else {
                users.stream().forEach(userMapper::add);
            }
        }
        return CommonResult.success(users.size());
    }

    /**
     * 添加用户
     *
     * @param userEntity
     * @return
     */
    @Override
    public CommonResult add(UserEntity userEntity) {
        //接收前台传来的数据，判断用户是否已经存在
        Boolean bool = findUserNameIsExists(userEntity.getUsername());
        if (bool) {
            //若存在则返回存在标识符
            return CommonResult.success("isHave");
        }
        //调用文件上传工具类
        String string = UploadFile.upload(userEntity.getFile());
        //存入本地项目中
        if (string != null) {
            userEntity.setIcon(string);
        }
        Integer result = userMapper.add(userEntity);
        if (result == 0) {
            return CommonResult.failed("新增失败！");
        }
        return CommonResult.success("新增成功！");
    }

    /**
     * 修改密码
     *
     * @param userEntity
     * @return
     */
    @Override
    public CommonResult updatePassword(UserEntity userEntity) {
        /**
         * 得到前台原密码，判断原密码和数据库中的密码是否相等，不相等则终止操作
         * 如果修改3次都不相等则冻结该账号24小时（待完成）
         */
        //原密码(加密)
        String oldPassword = passwordEncoder.encode(userEntity.getPassword());
        //拿到数据库中的密码
        String password = getPasswordByUsername(userEntity.getUsername());
        //对比密码是否相同
        boolean matcher = passwordEncoder.matches(oldPassword, password);
        if (!matcher) {
            return CommonResult.failed("原密码对比失败，请重新输入!");
        }
        //对比成功将新密码加密存入数据库中
        userEntity.setPassword(passwordEncoder.encode(userEntity.getNewPassword()));
        Integer result = userMapper.updatePassword(userEntity.getPassword());
        if (result == 0) {
            return CommonResult.failed("修改失败！");
        }
        return CommonResult.success("修改成功！");
    }

    /**
     * 得到学员列表
     *
     * @param userEntity
     * @return
     */
    @Override
    public CommonResult getStudent(UserEntity userEntity) {
        //得到查询总数
        Integer count = userMapper.getCount(userEntity);
        //分页，条件查询
        PageHelper.startPage(Tool.toPageNum(userEntity.getPageNum()), Tool.toPageSize(userEntity.getPageSize()));
        List<UserEntity> list = userMapper.getStudent(userEntity);
        PageInfo<UserEntity> pageInfo = new PageInfo<>(list);
        HashMap<String, Object> map = new HashMap<>();
        map.put("count", count);
        map.put("pageInfo", pageInfo);
        return CommonResult.success(map);
    }

    /**
     * 根据用户名查询用户id
     *
     * @param username
     */
    @Override
    public CommonResult findUserIdByUsername(String username) {
        UserEntity userEntity = userMapper.findUserIdByUsername(username);
        return CommonResult.success(userEntity);
    }

    /**
     * 根据用户名得到密码
     *
     * @param username
     * @return
     */
    public String getPasswordByUsername(String username) {
        String password = userMapper.getPasswordByUsername(username);
        return password;
    }
}


